using System.Diagnostics;
using System.Drawing;
using Microsoft.Extensions.Logging;
using _3Dto2D.Configuration;
using _3Dto2D.Interfaces;
using _3Dto2D.Models;

namespace _3Dto2D.Services;

public class ExportService
{
    private readonly IRenderEngine _renderEngine;
    private readonly ILibraryManager _libraryManager;
    private readonly ILogger<ExportService> _logger;
    private readonly RenderSettings _settings;
    private readonly BatchProcessor<FrameData, Bitmap?> _batchProcessor;

    public ExportService(
        IRenderEngine renderEngine,
        ILibraryManager libraryManager,
        ILogger<ExportService> logger,
        ILoggerFactory loggerFactory,
        RenderSettings settings)
    {
        _renderEngine = renderEngine;
        _libraryManager = libraryManager;
        _logger = logger;
        _settings = settings;
        _batchProcessor = new BatchProcessor<FrameData, Bitmap?>(
            loggerFactory.CreateLogger<BatchProcessor<FrameData, Bitmap?>>(),
            settings.MaxConcurrentRenders);
    }

    public async Task<ExportResult> ExportLibraryAsync(
        LibraryData sourceLibrary,
        string outputPath,
        int startFrame = 0,
        int? endFrame = null,
        IProgress<ExportProgressEventArgs>? progress = null,
        CancellationToken cancellationToken = default)
    {
        var stopwatch = Stopwatch.StartNew();
        var actualEndFrame = endFrame ?? sourceLibrary.Frames.Count;
        var framesToProcess = sourceLibrary.Frames
            .Skip(startFrame)
            .Take(actualEndFrame - startFrame)
            .ToList();

        _logger.LogInformation("Starting export of {FrameCount} frames to {OutputPath}", 
            framesToProcess.Count, outputPath);

        try
        {
            var outputLibrary = await _libraryManager.CreateLibraryAsync();
            var processedFrames = 0;

            var batchProgress = new Progress<BatchProgressEventArgs>(args =>
            {
                progress?.Report(new ExportProgressEventArgs
                {
                    CurrentFrame = args.Completed,
                    TotalFrames = args.Total,
                    Percentage = args.Percentage,
                    Message = $"Rendering frame {args.Completed} of {args.Total}",
                    ElapsedTime = stopwatch.Elapsed
                });
            });

            var renderedBitmaps = await _batchProcessor.ProcessBatchAsync(
                framesToProcess,
                async frame => await ProcessFrameAsync(frame, cancellationToken),
                batchProgress,
                cancellationToken);

            // Add rendered frames to output library
            for (int i = 0; i < renderedBitmaps.Count; i++)
            {
                var bitmap = renderedBitmaps[i];
                if (bitmap != null)
                {
                    var croppedResult = CropImage(bitmap);
                    await _libraryManager.AddFrameAsync(
                        outputLibrary, 
                        croppedResult.CroppedImage, 
                        croppedResult.OffsetX, 
                        croppedResult.OffsetY);
                    
                    bitmap.Dispose();
                    processedFrames++;
                }
            }

            // Save the library
            await _libraryManager.SaveLibraryAsync(outputPath, outputLibrary, false);

            stopwatch.Stop();

            var result = new ExportResult
            {
                Success = true,
                ProcessedFrames = processedFrames,
                TotalFrames = framesToProcess.Count,
                OutputPath = outputPath,
                ElapsedTime = stopwatch.Elapsed,
                Message = $"Successfully exported {processedFrames} frames"
            };

            _logger.LogInformation("Export completed successfully. Processed {ProcessedFrames} frames in {ElapsedTime:F2} seconds",
                processedFrames, stopwatch.Elapsed.TotalSeconds);

            return result;
        }
        catch (Exception ex)
        {
            stopwatch.Stop();
            _logger.LogError(ex, "Export failed after {ElapsedTime:F2} seconds", stopwatch.Elapsed.TotalSeconds);

            return new ExportResult
            {
                Success = false,
                ProcessedFrames = 0,
                TotalFrames = framesToProcess.Count,
                OutputPath = outputPath,
                ElapsedTime = stopwatch.Elapsed,
                Message = $"Export failed: {ex.Message}",
                Error = ex
            };
        }
    }

    private Task<Bitmap?> ProcessFrameAsync(FrameData frameData, CancellationToken cancellationToken)
    {
        return Task.Run(() =>
        {
            try
            {
                return _renderEngine.RenderFrameToBitmap(frameData);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to process frame {FrameIndex}", frameData.Index);
                return null;
            }
        }, cancellationToken);
    }

    private CropResult CropImage(Bitmap bitmap)
    {
        var backgroundColor = _settings.BackgroundColor;
        
        int minX = bitmap.Width;
        int maxX = 0;
        int minY = bitmap.Height;
        int maxY = 0;
        bool hasContent = false;

        // Find the bounds of non-background pixels
        for (int y = 0; y < bitmap.Height; y++)
        {
            for (int x = 0; x < bitmap.Width; x++)
            {
                var pixel = bitmap.GetPixel(x, y);
                if (pixel.R != backgroundColor.R || 
                    pixel.G != backgroundColor.G || 
                    pixel.B != backgroundColor.B)
                {
                    hasContent = true;
                    if (x < minX) minX = x;
                    if (x > maxX) maxX = x;
                    if (y < minY) minY = y;
                    if (y > maxY) maxY = y;
                }
            }
        }

        if (!hasContent)
        {
            // Return a 1x1 transparent image if no content found
            var emptyBitmap = new Bitmap(1, 1);
            emptyBitmap.SetPixel(0, 0, Color.Transparent);
            return new CropResult
            {
                CroppedImage = emptyBitmap,
                OffsetX = 0,
                OffsetY = 0
            };
        }

        var width = maxX - minX + 1;
        var height = maxY - minY + 1;
        var croppedBitmap = new Bitmap(width, height);

        using (var graphics = Graphics.FromImage(croppedBitmap))
        {
            graphics.DrawImage(bitmap, 
                new Rectangle(0, 0, width, height),
                new Rectangle(minX, minY, width, height),
                GraphicsUnit.Pixel);
        }

        return new CropResult
        {
            CroppedImage = croppedBitmap,
            OffsetX = (short)(minX - _settings.CharacterGlobalOffsetX),
            OffsetY = (short)(minY - _settings.CharacterGlobalOffsetY)
        };
    }
}

public class ExportResult
{
    public bool Success { get; set; }
    public int ProcessedFrames { get; set; }
    public int TotalFrames { get; set; }
    public string OutputPath { get; set; } = string.Empty;
    public TimeSpan ElapsedTime { get; set; }
    public string Message { get; set; } = string.Empty;
    public Exception? Error { get; set; }
}

public class ExportProgressEventArgs
{
    public int CurrentFrame { get; set; }
    public int TotalFrames { get; set; }
    public int Percentage { get; set; }
    public string Message { get; set; } = string.Empty;
    public TimeSpan ElapsedTime { get; set; }
}

public class CropResult
{
    public Bitmap CroppedImage { get; set; } = null!;
    public short OffsetX { get; set; }
    public short OffsetY { get; set; }
}
